package com.starryx.sim.mvc.controller;


import com.starryx.sim.mvc.annotation.RequestMapping;
import com.starryx.sim.mvc.annotation.enums.RequestMethod;
import com.starryx.sim.mvc.lifecycle.Lifecycle;
import com.starryx.sim.mvc.lifecycle.Priority;
import com.starryx.sim.mvc.util.SpringContextUtil;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

import java.util.*;
import java.util.stream.Collectors;


@Component
@Priority(2)
public class ControllerManager implements Lifecycle {

	/**
	 * key：请求映射包装类（请求路径，请求方法），value：处理方法的包装类
	 */
	private Map<RequestMappingWrapper, HandlerWrapper> handlerMap = new HashMap<>();

	/**
	 * 找到对应处理方法处理请求
	 *
	 * @param requestMappingWrapper 请求映射包装类
	 * @param param                 请求参数
	 * @return 处理结果
	 */
	public Object handleRequest(RequestMappingWrapper requestMappingWrapper, Object param) {
		HandlerWrapper handlerWrapper = handlerMap.get(requestMappingWrapper);
		if (handlerWrapper == null) {
			throw new RuntimeException("找不到处理请求的方法，请求路径：" + requestMappingWrapper.getUri() + "请求方法：" + requestMappingWrapper.getHttpMethod().name());
		}
		return handlerWrapper.hasRequestParam() ? handlerWrapper.invokeRequestParam((Map<String, Object>) param) : handlerWrapper.invoke(param);
	}

	@Override
	public void init() throws Throwable {
		Collection<Object> controllers = SpringContextUtil.getBeansWithAnnotation(Controller.class);
		for (Object controller : controllers) {
			initHandlerMapFromController(controller);
		}
	}

	@Override
	public void destroy() throws Throwable {

	}

	/**
	 * 查找对应处理方法参数的类型，或者说 json 需要转化的类型
	 *
	 * @param requestMappingWrapper 请求映射包装类
	 * @return 参数的类型
	 */
	public Class findParamType(RequestMappingWrapper requestMappingWrapper) {
		HandlerWrapper handlerWrapper = handlerMap.get(requestMappingWrapper);
		// 如果处理方法的参数中带有 RequestParam 注解，则将 json 转化为 Map<String, Object>
		return handlerWrapper.getParamType();
	}

	/**
	 * 检查对应处理方法是否带有 RequestParam 注解
	 *
	 * @param requestMappingWrapper 请求映射包装类
	 * @return 是否带有 RequestParam 注解
	 */
	public boolean checkRequestParam(RequestMappingWrapper requestMappingWrapper) {
		return handlerMap.get(requestMappingWrapper).hasRequestParam();
	}

	// todo：到时候考虑做成一个处理方法对应多个请求路径和多个请求方法

	/**
	 * 根据每一个控制器初始化 handlerMap
	 *
	 * @param controller 控制器对象
	 */
	// todo：在从注解中取出请求路径的时候，应该先判断请求路径是否以 / 开头，如果不是就加上斜杠，然后再判断是否以 / 结尾，如果是就将其去掉
	private void initHandlerMapFromController(Object controller) {
		Set<String> controllerUriSet = new HashSet<>();
		Set<RequestMethod> controllerMethodSet = new HashSet<>();
		// 先获取在 controller 类上的 RequestMapping 注解
		RequestMapping controllerMapping = controller.getClass().getAnnotation(RequestMapping.class);
		if (controllerMapping != null) {
			controllerUriSet.addAll(Arrays.stream(controllerMapping.value()).map(this::handleUriPrefixAndSuffix).collect(Collectors.toSet()));
			// 当 controller 上 RequestMapping 注解的请求方法属性不为空时，将属性中的值加入 methodSet 中
			if (controllerMapping.method().length != 0) {
				controllerMethodSet.addAll(Arrays.asList(controllerMapping.method()));
			}
		}
		Arrays.asList(controller.getClass().getDeclaredMethods()).forEach((method) -> {
			// todo：之后可以考虑抽取一个方法
			/* 以下是处理带有 RequestMapping 注解的方法 */
			RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
			if (requestMapping != null) {
				HandlerWrapper handlerWrapper = new HandlerWrapper(controller, method);
				Set<String> handlerUriSet = new HashSet<>();
				if (controllerUriSet.isEmpty()) {
					handlerUriSet.addAll(Arrays.asList(requestMapping.value()));
				} else {
					for (String baseUri : controllerUriSet) {
						// 将每一个注在 controller 上的请求路径都与每一个注在方法上的请求路径拼接，放进 handlerUriSet 中
						handlerUriSet.addAll(Arrays.stream(requestMapping.value())
								.map(this::handleUriPrefixAndSuffix)
								.map(uri -> baseUri + uri).collect(Collectors.toSet()));
					}
				}
				Set<RequestMethod> handlerMethodSet = new HashSet<>();
				if (requestMapping.method().length == 0 && controllerMethodSet.isEmpty()) {
					handlerMethodSet.addAll(Arrays.asList(RequestMethod.values()));
				} else if (requestMapping.method().length == 0 && !controllerMethodSet.isEmpty()) {
					handlerMethodSet.addAll(controllerMethodSet);
				} else {
					handlerMethodSet.addAll(Arrays.asList(requestMapping.method()));
				}
				for (String uri : handlerUriSet) {
					// 如果处理方法上 RequestMapping 注解的请求方法属性为空，则默认设为所有的请求方法，否则就按属性值来
					for (RequestMethod requestMethod : handlerMethodSet) {
						RequestMappingWrapper requestMappingWrapper = new RequestMappingWrapper(uri, requestMethod.getMethod());
						handlerMap.put(requestMappingWrapper, handlerWrapper);
					}
				}
			}
		});
	}

	/**
	 * 处理 uri 的前缀和后缀问题，先判断请求路径是否以 / 开头，如果不是就加上斜杠，然后再判断是否以 / 结尾，如果是就将其去掉
	 * @param uri 待处理的 uri
	 * @return 处理好的 uri
	 */
	private String handleUriPrefixAndSuffix(String uri) {
		if (uri.endsWith("/")) {
			uri = uri.substring(0, uri.length() - "/".length());
		}
		if (!uri.startsWith("/")) {
			uri = "/" + uri;
		}
		return uri;
	}

}
